package server;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Random;

import server.locations.Planet;
import server.locations.PlanetType;
import server.locations.Star;
import server.locations.StarColor;
import server.players.Player;
import server.players.PlayerGroup;
import server.players.PlayerGroupingException;
import kryonetpackets.KickPacket;
import kryonetpackets.LoginConfirm;
import kryonetpackets.LoginRequest;
import kryonetpackets.MessagePacket;
import kryonetpackets.NetworkPlayer;
import kryonetpackets.PlayerGroupExitedPlanet;
import kryonetpackets.PlayerGroupExitedWorld;
import kryonetpackets.PlayerGroupJoinedPlanet;
import kryonetpackets.PlayerGroupJoinedWorld;
import kryonetpackets.RGB;

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryonet.*;

import database.DBConnector;
import server.MainGUI;

public class GameServer {
	
	private Server server = null;
	private boolean isRunning;
	private HashMap<Integer, Player> onlinePlayers;
	private HashMap<Integer, String> onlineUsers;
	private MainGUI mainGUI;
	private DBConnector dbc;
	
	private Planet planet;
	private Star star;
	
	public GameServer(MainGUI gui)
	{
		this.mainGUI = gui;
		this.isRunning = false;
		
		dbc = new DBConnector();
		
		this.onlinePlayers = new HashMap<Integer, Player>();
		this.onlineUsers = new HashMap<Integer,String>();
		planet = new Planet("Homeworld", PlanetType.FOREST);
		star = new Star("Thrison 1337",0,0, StarColor.WHITE);
		star.addPlanet(planet);
	}
	public void Kick(int id)
	{
		server.sendToTCP(id, new KickPacket());
	}

	public void startServer()
	{
		if (!isRunning) {
			//Start the server
			server = new Server();
			server.start();
			try {
				server.bind(8078,8079);
			} catch (IOException e) {
				e.printStackTrace();
			}
			isRunning = true;
			System.out.println("Sever started!");
			
			//Register the packets
			Kryo kryo = server.getKryo();
			kryo.register(LoginRequest.class);
			kryo.register(LoginConfirm.class);
			kryo.register(KickPacket.class);
			kryo.register(PlayerGroupJoinedWorld.class);
			kryo.register(PlayerGroupJoinedPlanet.class);
			kryo.register(PlayerGroupExitedWorld.class);
			kryo.register(PlayerGroupExitedPlanet.class);
			kryo.register(NetworkPlayer.class);
			kryo.register(MessagePacket.class);
			kryo.register(RGB.class);
			kryo.register(List.class);
			kryo.register(ArrayList.class);
			kryo.register(Random.class);
			//kryo.register(class.class);
			
			//The actual server listener
			server.addListener(new Listener() {
				public void received(Connection connection, Object object)
				{
					if (object instanceof LoginRequest)
					{
						LoginRequest request = (LoginRequest) object;
						//TODO: Either the client will have to close when the
						//		player logs out, or this with need to check
						//		against player info once database is implemented
						// Prevent duplicate logins
						if (onlinePlayers.containsKey(connection.getID())) {
							connection.sendTCP(new LoginConfirm(true));
						}
						/*else if (request.getUserName() != null &&
								request.getUserName() != "" &&
								request.getPassword() != null &&
								request.getPassword() != "")*/
						
						if ((dbc.checkLogin(request.getUserName(), request.getPassword())) &&
								!onlineUsers.containsValue(request.getUserName()))
						{
							//TODO: fix player construction when database is ready
							Player p = new Player(connection.getID(), "Player",
								new RGB(), connection.getID());
							p.setName(p.getName() + p.getConnectionId());
							onlinePlayers.put(connection.getID(), p);
							onlineUsers.put(connection.getID(),request.getUserName());
							
							mainGUI.addPlayerToOnlineList(p);
							try {
								//TODO: get last planet from database
								PlayerGroup pg = new PlayerGroup(p);
								planet.addPlayerGroup(pg);
							} catch (PlayerGroupingException e) {
								e.printStackTrace();
							}	
						
							connection.sendTCP(new LoginConfirm(true,
									new NetworkPlayer(p.getName(),p.getColor())));
							connection.sendTCP(new MessagePacket("Server","Welcome to the Server!"));
							//Send to everyone that a new player joined the world
							//TODO Change this to only relay to people with the
							//		joining player on their contacts list
							server.sendToAllTCP(new PlayerGroupJoinedWorld(
									new NetworkPlayer(p.getName(),p.getColor())));
							//Send to the client that it joined the planet
							connection.sendTCP(new PlayerGroupJoinedPlanet(
									new NetworkPlayer(p.getName(),p.getColor())));
							
							for(Player temp : onlinePlayers.values()) {
							    if (p != temp) {
							    	connection.sendTCP(new PlayerGroupJoinedWorld(
							    			new NetworkPlayer(temp.getName(),temp.getColor())));
							    }
							    //TODO Fix this once server/database has planets
							    if (planet.hasPlayer(temp) && planet.hasPlayer(p) && p != temp){
							    	server.sendToTCP(temp.getConnectionId(),
							    			new PlayerGroupJoinedPlanet(
							    					new NetworkPlayer(p.getName(),p.getColor())));
							    	connection.sendTCP(new PlayerGroupJoinedPlanet(
							    			new NetworkPlayer(temp.getName(),temp.getColor())));
							    	
							    }
							   
							    
							}
						}
						else {
							connection.sendTCP(new LoginConfirm(false));
						}
					}
					
					
					else if (object instanceof MessagePacket)
					{
						MessagePacket message = (MessagePacket) object;
						server.sendToAllTCP(message);
					}
				}
				public void disconnected(Connection connection)
				{
					Player p = onlinePlayers.get(connection.getID());
					//TODO: clean player data first
					if (p != null) {
						//TODO: Make it less ugly
						for(Player player : onlinePlayers.values()) {
						    if (planet.hasPlayer(player) &&
						    		planet.hasPlayer(p) && p != player){
						    	server.sendToTCP(player.getConnectionId(),
						    			new PlayerGroupExitedPlanet(
						    					new NetworkPlayer(p.getName(),
						    							p.getColor())));
						    }
						    
						}
						
						onlinePlayers.remove(connection.getID());
						mainGUI.removePlayerFromOnlineList(p);
						p.removeFromAllPlayerGroups();
						onlineUsers.remove(connection.getID());
						
					
						server.sendToAllTCP(new PlayerGroupExitedWorld(
								new NetworkPlayer(p.getName(),p.getColor())));
					}
				}
				
				public void connected(Connection connection)
				{
					System.out.println(connection.getID() + " connected!");
				}
			});
		}
		
	}
	public void stopServer()
	{
		if (isRunning) {
			server.stop();
			server.close();
		}
	}
	
	
	
	
	

}
